package edu.snake.implement.model.character;

import edu.snake.core.model.character.Character;
import edu.snake.core.model.character.CharacterState;
import edu.snake.core.model.character.Course;
import edu.snake.core.model.character.Movement;
import edu.snake.core.model.character.StateCharacter;
import edu.snake.implement.model.CharacterName;
import edu.snake.implement.model.arena.Direction;
import edu.snake.implement.model.character.movement.CourseEmptyImpl;
import edu.snake.implement.model.character.movement.NullMove;
import edu.snake.implement.model.character.state.CharacterStateNotElement;
import edu.snake.implement.model.character.state.CharacterStateWithElement;

import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;


public class CharacterImpl implements Character {

    private static final AtomicInteger idCount = new AtomicInteger(0);

    protected final int id;

    protected final CharacterName characterName;
    protected boolean alive;

    protected Course course;
    protected Movement movement;

    protected final StateCharacter noElementsState;
    protected final StateCharacter withElementsState;
    protected StateCharacter currentState;


    public CharacterImpl(CharacterName characterName) {
        this.characterName = characterName;
        noElementsState = new CharacterStateNotElement(this);
        withElementsState = new CharacterStateWithElement(this);
        currentState = noElementsState;
        course = new CourseEmptyImpl();
        movement = new NullMove();
        this.id = idCount.incrementAndGet();

    }

    public CharacterImpl(CharacterName characterName, Course course) {
        this(characterName);
        this.course = course;
    }


    @Override
    public Character move() {
        return movement.performMovement();
    }

    @Override
    public int getId() {
        return id;
    }

    @Override
    public CharacterName getName() {
        return characterName;
    }

    @Override
    public boolean isAlive() {
        return alive;
    }

    @Override
    public void setAlive(boolean alive) {
        this.alive = alive;
    }

    @Override
    public Direction getDirection() {
        return course.calculateCourse();
    }

    @Override
    public void setDirection(Direction direction) {
        course.setDirection(direction);
    }

    @Override
    public Iterator<Character> iterator() {
        return currentState.iterator();
    }


    @Override
    public void add(Character character) {
        currentState.add(character);
    }

    @Override
    public void remove(Character character) {
        currentState.remove(character);
    }

    @Override
    public Character getChild(int i) {
        return currentState.getChild(i);
    }

    @Override
    public int size() {
        return currentState.size();
    }

    @Override
    public CharacterState getState() {
        return currentState.getState();
    }

    @Override
    public void setState(CharacterState characterState) {
        switch (characterState) {
            case NOT_ELEMENT:
                if (this.getState() == CharacterState.WITH_ELEMENT && this.iterator().hasNext()) {
                    throw new UnsupportedOperationException("This character contains element. " +
                            "All elements must be remove before change state form WITH_ELEMENT to NOT_ELEMENT");
                } else if (this.getState() == CharacterState.WITH_ELEMENT) {
                    this.currentState = noElementsState;
                }
                break;
            case WITH_ELEMENT:
                if (this.getState() == CharacterState.NOT_ELEMENT) {
                    this.currentState = withElementsState;
                }
        }
    }

    @Override
    public void setMovement(Movement movement) {
        this.movement = movement;
    }

    @Override
    public void setCourse(Course course) {
        this.course = course;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof CharacterImpl)) return false;

        CharacterImpl character = (CharacterImpl) o;

        return id == character.id;

    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public String toString() {
        return "CharacterImpl{" +
                "id=" + id +
                ", characterName=" + characterName +
                '}';
    }
}
